#include "StdAfx.h"

#include <MyControls/PropertyRow.h>
#include <MyControls/PEditor.h>
#include <MyFC/Utility.h>

#include <algorithm>
#include <xtd/functional>

namespace My {

const int Indent			= 16;
const int ButtonSize		= 13;

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

PropertyRow::PropertyRow(PEditor & e, const String & s, PropertyBase * pProperty, const EditableList & objects, PropertyRow * pParent) : 
	RowControl(e.m_List), 
	m_Editor(e), 
	m_List(e.m_List), 
	m_Caption(s), 
	m_pProperty(pProperty), 
	m_Objects(objects),
	m_pParent(pParent),
	m_Valid(true)
{
	m_Expanded = false;
	SetRowHeight(GetDefaultRowHeight());
	m_pControl = m_pProperty->CreateControl(m_Objects);
	if (m_pControl != 0)
		Add(m_pControl);
	m_pSubProperties = m_pProperty->GetSubProperties(m_Objects);
	m_pProperty->GetObjects(m_Objects, m_SubObjects);
	if (m_pSubProperties != 0)
		CreateChildren(* m_pSubProperties, m_SubObjects);
	m_pProperty->Attach(this);
}

PropertyRow::~PropertyRow()
{
	m_pProperty->Detach(this);

	std::for_each(m_Children.begin(), m_Children.end(),	xtd::deleter());
	m_Children.clear();

	m_pProperty->FreeControl(m_pControl);
}

bool PropertyRow::IsExpandable() const
{
	return !m_Children.empty();
}

int PropertyRow::GetLevel() const
{
	return m_pParent? m_pParent->GetLevel() + 1 : 0;
}

int PropertyRow::GetDefaultRowHeight() const
{
	const CFont * pFont = Font();
	CDC dc;
	dc.CreateCompatibleDC(0);
	SaveObjects save(&dc);
	dc.SelectObject(const_cast<CFont *>(pFont));
	return dc.GetTextExtent("A").cy + 4;
}

void PropertyRow::Expand()
{
	if (m_Expanded)
		return;
	m_List.InsertRows(GetRowIndex() + 1, &m_Children[0], m_Children.size());
	m_Expanded = true;
	Invalidate();
	for (My::Size i = 0; i < m_Children.size(); ++i) {
		PropertyRow * pRow = static_cast<PropertyRow *>(m_Children[i]);
		bool expanded = pRow->GetProperty()->DefaultExpanded();
		PEditor::StateMap::iterator it = m_Editor.m_StateMap.find(pRow->GetProperty());
		if (it != m_Editor.m_StateMap.end())
			expanded = it->second;
		if (expanded)
			pRow->Expand();
	}
}

void PropertyRow::Collapse()
{
	if(!m_Expanded)
		return;
	for (My::Size i = 0; i < m_Children.size(); ++i) {
		PropertyRow * pRow = static_cast<PropertyRow *>(m_Children[i]);
		m_Editor.m_StateMap[pRow->GetProperty()] = pRow->IsExpanded();
		if (pRow->IsExpanded())
			pRow->Collapse();
	}
	m_List.RemoveRows(GetRowIndex() + 1, m_Children.size());
	m_Expanded = false;
	Invalidate();
}

void PropertyRow::LayoutControls()
{
	if (m_pControl != 0) {
		CRect rControl = ClientRect();
		rControl.left = m_Editor.GetCaptionWidth() + 4;
		m_pControl->SetBounds(rControl);
	}
}

void PropertyRow::HandlePaint(CDC * pDC, const CRect & r)
{
	My::SaveObjects save(pDC);

	CRect rClient = ClientRect();

	COLORREF cS	= GetSysColor(COLOR_BTNSHADOW);
	COLORREF cF	= GetSysColor(COLOR_BTNFACE);
	COLORREF cLF = My::MeanSysColor(COLOR_BTNFACE, COLOR_BTNHILIGHT);
	COLORREF cL = GetSysColor(COLOR_BTNHILIGHT);
	COLORREF cViolet = RGB(224, 224, 255);

	pDC->SetBkMode(TRANSPARENT);

	CBrush bGround;
	if (GetRowIndex() != m_List.m_ActiveRow)
		bGround.CreateSolidBrush(cLF);
	else
		bGround.CreateSolidBrush(cViolet);
	pDC->FillRect(&rClient, &bGround);

	CRect rCaption(rClient);

	rCaption.right = m_Editor.GetCaptionWidth();
	rCaption.left =	ButtonSize + GetLevel()	* Indent;

	if (!rCaption.IsRectEmpty()) {

		CRect rText(rCaption);
		rText.InflateRect(-4, 0);

		SelectFont(pDC);
		COLORREF cOld;
		if (ControlEnabled(this))
			cOld = pDC->SetTextColor(GetSysColor(COLOR_WINDOWTEXT));
		else
			cOld = pDC->SetTextColor(GetSysColor(COLOR_BTNSHADOW));

		pDC->DrawText(m_Caption.c_str(), &rText, DT_SINGLELINE|DT_END_ELLIPSIS|DT_LEFT|DT_VCENTER);
		
		pDC->DrawText(m_Caption.c_str(), &rText, DT_SINGLELINE|DT_END_ELLIPSIS|DT_CALCRECT);
		rText = My::AlignRect(rCaption, rText, My::ALIGN_VCENTER);
		rText.InflateRect(2, 1);

		pDC->SetTextColor(cOld);

		if (GetRowIndex() == m_List.m_ActiveRow && 
			m_List.HasFocus())
			pDC->DrawFocusRect(&rText);

		if (IsExpandable())	{

			m_ButtonRect = rCaption;

			m_ButtonRect.right	= rCaption.left;
			m_ButtonRect.left	= m_ButtonRect.right - ButtonSize + 2;
			m_ButtonRect.bottom -= 2;

			CPoint p = m_ButtonRect.CenterPoint();

			if (IsExpanded())
				m_Editor.m_ButtonDown.Draw(pDC, p.x, p.y, My::ALIGN_HCENTER|My::ALIGN_VCENTER);
			else
				m_Editor.m_ButtonUp.Draw(pDC, p.x, p.y, My::ALIGN_HCENTER|My::ALIGN_VCENTER);
		}
	}

	CRect rControl(rClient);
	rControl.left =	rCaption.right;

	if (!rControl.IsRectEmpty()) 
	{
		if (m_pControl != 0) {
			CBrush bF(cF);
			pDC->FillRect(&rControl, &bF);
		}
	}

	CPen pLight(PS_SOLID, 1, cL);
	pDC->SelectObject(&pLight);
	pDC->MoveTo(rClient.left, rClient.bottom - 1);
	pDC->LineTo(rClient.right, rClient.bottom - 1);

	CPen pShadow(PS_SOLID, 1, cS);
	pDC->SelectObject(&pShadow);
	pDC->MoveTo(rCaption.right, rClient.top);
	pDC->LineTo(rCaption.right, rClient.bottom);

}

void PropertyRow::CreateChildren(const PropertyMap & map, const EditableList & objects)
{
	PropertyMap::const_iterator it;
	for (it = map.begin(); it != map.end(); ++it) {
		m_Children.push_back(
			new PropertyRow(m_Editor, (* it).first, (* it).second, objects, this)
		);
	}
}

void PropertyRow::Update()
{
	const PropertyMap *	pSubProperties = m_pProperty->GetSubProperties(m_Objects);
	EditableList subObjects;
	m_pProperty->GetObjects(m_Objects, subObjects);

	if (pSubProperties != m_pSubProperties 
		|| subObjects != m_SubObjects) 
	{
		Invalidate();
		m_pSubProperties = pSubProperties;
		m_SubObjects = subObjects;

		bool wasExpanded = IsExpanded();
		Collapse();
		std::for_each(m_Children.begin(), m_Children.end(),	xtd::deleter());
		m_Children.clear();
		if (m_pSubProperties != 0)
			CreateChildren(* m_pSubProperties, m_SubObjects);
		if (wasExpanded)
			Expand();
	} else {
		Invalidate();
		for (My::Size i = 0; i < m_Children.size(); ++i) {
			PropertyRow * pRow = static_cast<PropertyRow *>(m_Children[i]);
			pRow->Update();
		}
	}
	
	m_Valid = true;
}


void PropertyRow::HandleLeftDoubleClick(unsigned int flags, const CPoint & p)
{
	RowControl::HandleLeftDoubleClick(flags, p);
	if(IsExpandable())
	{
		if(IsExpanded())
			Collapse();
		else
			Expand();
	}
}

void PropertyRow::HandleLeftDown(unsigned int flags, const CPoint & p)
{
	RowControl::HandleLeftDown(flags, p);
	if (IsExpandable() && m_ButtonRect.PtInRect(p)) {
		if(IsExpanded())
			Collapse();
		else
			Expand();
	} else {
		m_List.SetActiveRow(GetRowIndex());
	}
}

void PropertyRow::ProcessEvent(Event & e)
{
	if (e.Is<PropertyChange>()) {
		m_Valid = false;
		//Invalidate();
		//Update();
		return;
	}
	Container::ProcessEvent(e);
}

bool PropertyRow::Enabled() const
{
	return true; //!m_pProperty->ReadOnly(m_Objects);
}

void PropertyRow::HandleUpdateUI()
{
	if (!m_Valid)
		Update();
}

} // My